package com.guanmi.ethereum.service.impl;

import java.io.File;
import java.math.BigDecimal;
import java.math.BigInteger;
import java.util.List;

import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.web3j.crypto.Credentials;
import org.web3j.crypto.RawTransaction;
import org.web3j.crypto.TransactionEncoder;
import org.web3j.crypto.WalletUtils;
import org.web3j.protocol.Web3j;
import org.web3j.protocol.admin.Admin;
import org.web3j.protocol.core.DefaultBlockParameterName;
import org.web3j.protocol.core.methods.request.Transaction;
import org.web3j.protocol.core.methods.response.EthGasPrice;
import org.web3j.protocol.core.methods.response.EthGetBalance;
import org.web3j.protocol.core.methods.response.EthGetTransactionCount;
import org.web3j.protocol.core.methods.response.EthSendTransaction;
import org.web3j.protocol.core.methods.response.EthTransaction;
import org.web3j.protocol.core.methods.response.TransactionReceipt;
import org.web3j.protocol.http.HttpService;
import org.web3j.tx.ClientTransactionManager;
import org.web3j.tx.Transfer;
import org.web3j.utils.Convert;
import org.web3j.utils.Numeric;

import com.alibaba.fastjson.JSON;
import com.guanmi.ethereum.configure.EthConfigure;
import com.guanmi.ethereum.constants.GlobalConstants;
import com.guanmi.ethereum.domain.ERC20TokenSolTemplate;
import com.guanmi.ethereum.domain.WalletResponseEntity;
import com.guanmi.ethereum.service.EthereumService;

@Service
public class EthereumServiceImpl implements EthereumService {

	private static final Logger log = LoggerFactory.getLogger(EthereumServiceImpl.class);

	private static final BigInteger GAS_PRICE = BigInteger.valueOf(22_000_000_000L);

	private static final BigInteger GAS_LIMIT = BigInteger.valueOf(4_300_000);
	
	
	@Autowired
	private EthConfigure  ethConfigure;

	/**
	 * 获取账户列表
	 */
	public WalletResponseEntity<List<String>> getAccountlist()  {
		WalletResponseEntity<List<String>> result = new WalletResponseEntity<List<String>>();
		try {
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			result.setData(web3j.ethAccounts().send().getAccounts());
			return result;
		} catch (Exception e) {
			log.error("获取账户列表异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}

	}

	/**
	 * 创建帐户
	 */
	public WalletResponseEntity<Credentials> generateNewWalletFile(String password)  {
		WalletResponseEntity<Credentials> result = new WalletResponseEntity<Credentials>();
		try {
			String fileName = WalletUtils.generateNewWalletFile(password,new File(ethConfigure.getDestinationDirectory()),false);
			Credentials credentials = WalletUtils.loadCredentials(password, ethConfigure.getDestinationDirectory()+File.separator+fileName);
			result.setData(credentials);
			return result;
		} catch (Exception e) {
			log.error("创建帐户异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}

	/**
	 * 获取帐户余额
	 */
	public WalletResponseEntity<BigInteger> getBalance(String address )  {
		 WalletResponseEntity<BigInteger>  result = new  WalletResponseEntity<BigInteger> ();
		try {
			Admin admin = Admin.build(new HttpService(ethConfigure.getNodeUrl()));
			EthGetBalance ethGetBalance = admin.ethGetBalance(address, DefaultBlockParameterName.LATEST).send();
			result.setData(ethGetBalance.getBalance());
			return result;
		} catch (Exception e) {
			log.error("获取账户余额异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}

	/**
	 * 获取用户交易记录数
	 */
	public WalletResponseEntity<BigInteger> getTransactionCount(String address )  {
		 WalletResponseEntity<BigInteger>  result = new  WalletResponseEntity<BigInteger> ();
		try {
			Admin admin = Admin.build(new HttpService(ethConfigure.getNodeUrl()));
			EthGetTransactionCount ethGetTransactionCount = admin .ethGetTransactionCount(address, DefaultBlockParameterName.LATEST).sendAsync().get();
			if (null == ethGetTransactionCount) {
				log.error("失获取用户交易记录数败:{}", address);
				result.setStatus(GlobalConstants.PARAM_ERROR);
				return result;
			}
			result.setData(ethGetTransactionCount.getTransactionCount());
			return result;
		} catch (Exception e) {
			log.error("获取用户交易记录数异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}

	/**
	 * 获取交易信息
	 */
	public WalletResponseEntity<EthTransaction> getTransaction(String transactionHash )  {
		WalletResponseEntity<EthTransaction>  result = new WalletResponseEntity<EthTransaction>();
		try {
			Admin admin = Admin.build(new HttpService(ethConfigure.getNodeUrl()));
			result.setData(admin.ethGetTransactionByHash(transactionHash).sendAsync().get());
			return result;
		} catch (Exception e) {
			log.error("获取交易信息异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}

	/**
	 * 获取用户账号
	 */
	public WalletResponseEntity<String> getAddress(String walletfile, String password )  {
		WalletResponseEntity<String>  result = new WalletResponseEntity<String>();
		try {
			Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
			result.setData(credentials.getAddress());
			return result;
		} catch (Exception e) {
			log.error("获取用户帐号异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
   /**
    * 转账
    */
	public WalletResponseEntity<EthSendTransaction> ethSendRawTransaction(String walletfile, String password, String toAddress,
	        BigDecimal coinNum )  {
		WalletResponseEntity<EthSendTransaction> result = new WalletResponseEntity<EthSendTransaction>();
		String 	fromAddress="";
		try {
			Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
		 	fromAddress = credentials.getAddress();
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			EthGetTransactionCount ethGetTransactionCount = web3j.ethGetTransactionCount(credentials.getAddress(), DefaultBlockParameterName.LATEST).sendAsync().get();
			BigInteger nonce = ethGetTransactionCount.getTransactionCount();
			BigInteger weiValue = Convert.toWei(coinNum, Convert.Unit.ETHER).toBigInteger();
			
			 RawTransaction rawTransaction = RawTransaction.createEtherTransaction(
		                nonce, GAS_PRICE, GAS_LIMIT, toAddress, weiValue);
			//签名Transaction，这里要对交易做签名
	        byte[] signedMessage = TransactionEncoder.signMessage(rawTransaction, credentials);
	        String hexValue = Numeric.toHexString(signedMessage);
	        //发送交易
	        EthSendTransaction ethSendTransaction =web3j.ethSendRawTransaction(hexValue).sendAsync().get();
			result.setData(ethSendTransaction); 
           return result;
		} catch (Exception e) {
			log.error("[{}]转账给[{}]{}个ETH异常:{}", fromAddress, toAddress, coinNum, e.toString());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
	/**
	 * 合约转账
	 */
	public WalletResponseEntity<TransactionReceipt> tokenERC20SendPassword(String deployeraccount,String password, String contractAddress, String toAddress,
	        BigDecimal coinNum)  {
		WalletResponseEntity<TransactionReceipt> result = new WalletResponseEntity<TransactionReceipt>();
		try {
			Admin admin = Admin.build(new HttpService(ethConfigure.getNodeUrl()));
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			ClientTransactionManager transactionManager = new ClientTransactionManager(web3j,deployeraccount);
			admin.personalUnlockAccount(deployeraccount, password).send();
			ERC20TokenSolTemplate sol_tokenERC20 = ERC20TokenSolTemplate.load(contractAddress,web3j,transactionManager,GAS_PRICE, GAS_LIMIT);
			BigInteger weiValue = Convert.toWei(coinNum, Convert.Unit.ETHER).toBigInteger();
			TransactionReceipt transactionReceipt = sol_tokenERC20.transferFrom(deployeraccount,toAddress, weiValue).send();
			result.setData(transactionReceipt);
			return result;
		} catch (Exception e) {
			log.error("[{}]转账给[{}]{}个ETH异常:{}", deployeraccount, toAddress, coinNum, e.toString());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
	
	/**
	 * 合约转账
	 */
	public WalletResponseEntity<TransactionReceipt> tokenERC20SendWalletFile(String walletfile,String password, String contractAddress, String toAddress,
	        BigDecimal coinNum)  {
		WalletResponseEntity<TransactionReceipt> result = new WalletResponseEntity<TransactionReceipt>();
		try {
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
			ERC20TokenSolTemplate sol_tokenERC20 = ERC20TokenSolTemplate.load(contractAddress,web3j,credentials,GAS_PRICE, GAS_LIMIT);
			BigInteger weiValue = Convert.toWei(coinNum, Convert.Unit.ETHER).toBigInteger();
			TransactionReceipt transactionReceipt = sol_tokenERC20.transferFrom(credentials.getAddress(),toAddress, weiValue).send();
			result.setData(transactionReceipt);
			return result;
		} catch (Exception e) {
			log.error("转账给[{}]个ETH异常:{}",  toAddress, coinNum, e.toString());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
	
	
	
	/**
	 * 部署合约
	 */
	public WalletResponseEntity<ERC20TokenSolTemplate> deployContract(String deployeraccount, BigDecimal initialSupply,
			String tokenName, String tokenSymbol)  {
			WalletResponseEntity<ERC20TokenSolTemplate>  result = new WalletResponseEntity<ERC20TokenSolTemplate>();
			try {
				Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
				ClientTransactionManager transactionManager = new ClientTransactionManager(web3j,deployeraccount);
				BigInteger initialSupplyValue = Convert.toWei(initialSupply, Convert.Unit.ETHER).toBigInteger();
				 ERC20TokenSolTemplate tokenERC20 = ERC20TokenSolTemplate.deploy(web3j,transactionManager, GAS_PRICE, GAS_LIMIT,initialSupplyValue,tokenName,tokenSymbol).send();
		         System.out.println(JSON.toJSONString(tokenERC20));
		         result.setData(tokenERC20);
		         return result;
			} catch (Exception e) {
				log.error("部署合约异常{}{}{}{}", deployeraccount, initialSupply, tokenName, tokenSymbol,e);
				result.setStatus(GlobalConstants.SYSTEM_ERROR);
				return result;
			}

		}
	
	/**
	 * 部署合约
	 */
	public WalletResponseEntity<ERC20TokenSolTemplate> deployContract(String walletfile, String password ,BigDecimal initialSupply,
			String tokenName, String tokenSymbol)  {
			WalletResponseEntity<ERC20TokenSolTemplate>  result = new WalletResponseEntity<ERC20TokenSolTemplate>();
			try {
				Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
				Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
				BigInteger initialSupplyValue = Convert.toWei(initialSupply, Convert.Unit.ETHER).toBigInteger();
				 ERC20TokenSolTemplate tokenERC20 = ERC20TokenSolTemplate.deploy(web3j,credentials, GAS_PRICE, GAS_LIMIT,initialSupplyValue,tokenName,tokenSymbol).send();
		         System.out.println(JSON.toJSONString(tokenERC20));
		         result.setData(tokenERC20);
		         return result;
			} catch (Exception e) {
				log.error("部署合约异常{}{}{}", initialSupply, tokenName, tokenSymbol,e);
				result.setStatus(GlobalConstants.SYSTEM_ERROR);
				return result;
			}

		}
	
	public WalletResponseEntity<EthSendTransaction> personalSendTransaction(String fromAddress, String fromPassword, String toAddress,
	        BigDecimal coinNum)  {
		WalletResponseEntity<EthSendTransaction> result = new WalletResponseEntity<EthSendTransaction>();
		try {
			Admin admin = Admin.build(new HttpService(ethConfigure.getNodeUrl()));
			// 获取nonce
			EthGetTransactionCount ethGetTransactionCount = admin
			        .ethGetTransactionCount(fromAddress, DefaultBlockParameterName.LATEST).sendAsync().get();

			BigInteger nonce = ethGetTransactionCount.getTransactionCount();

			// 进行单位换算
			BigDecimal weiValue = Convert.toWei(coinNum, Convert.Unit.ETHER);

			// 创建交易记录实体 Transfer.GAS_PRICE, Transfer.GAS_LIMIT
			Transaction transaction = Transaction.createEtherTransaction(fromAddress, nonce, GAS_PRICE, GAS_LIMIT,
			        toAddress, weiValue.toBigInteger());

			EthSendTransaction ethSendTransaction = admin.personalSendTransaction(transaction, fromPassword).sendAsync()
			        .get();
			if (null == ethSendTransaction) {
				log.error("转账异常");
				result.setStatus(GlobalConstants.SYSTEM_ERROR);
				return result;
			} else if (null != ethSendTransaction.getError()) {
				log.error("[{}]转账给[{}]{}个ETH异常:{}", fromAddress, toAddress, coinNum,
				        ethSendTransaction.getError().getMessage());
				result.setStatus(GlobalConstants.SYSTEM_ERROR);
				return result;
			}
			result.setData(ethSendTransaction);
			return result;
		} catch (Exception e) {
			log.error("[{}]转账给[{}]{}个ETH异常:{}", fromAddress, toAddress, coinNum, e.toString());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
   /**
    * 转账
    */
	public WalletResponseEntity<TransactionReceipt> transferSendFunds(String walletfile, String password, String toAddress,
	        BigDecimal coinNum)  {
		WalletResponseEntity<TransactionReceipt>  result = new WalletResponseEntity<TransactionReceipt>();
		String fromAddress = "";
		try {
			Credentials credentials = WalletUtils.loadCredentials(password, walletfile);
			fromAddress = credentials.getAddress();
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			TransactionReceipt transactionReceipt = Transfer
			        .sendFunds(web3j, credentials, toAddress, coinNum, Convert.Unit.ETHER).sendAsync().get();
			result.setData(transactionReceipt);
			return result;
		} catch (Exception e) {
			log.error("[{}]转账给[{}]{}个ETH异常:{}", fromAddress, toAddress, coinNum, e.toString());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}

	}
	
	
	
    /**
     * 获取加油费
     */
	public WalletResponseEntity<EthGasPrice> ethGasPrice()  {
		 WalletResponseEntity<EthGasPrice> result = new  WalletResponseEntity<EthGasPrice>();
		try {
			Web3j web3j = Web3j.build(new HttpService(ethConfigure.getNodeUrl()));
			result.setData(web3j.ethGasPrice().sendAsync().get());
			return result;
		} catch (Exception e) {
			log.error("获取EthGasPrice异常:{}", e.getMessage());
			result.setStatus(GlobalConstants.SYSTEM_ERROR);
			return result;
		}
	}
}
